home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / ansi_130.zip / MUSICA.PAS < prev    next >
Pascal/Delphi Source File  |  1990-06-03  |  14KB  |  483 lines

  1. { $A+,B-,D-,E-,F+,I-,L-,O+,R-,S-,V-}
  2. (*
  3.     Musica v1.00 (c) CopyRight P.H.Rankin Hansen 1990.
  4.  
  5.     This unit implements the Play  statement knovn from Basic in Turbo
  6.     Pascal  versions  5.x  and  higher.  (version  4  does not support
  7.     procedural types). The syntax adhers  to the Basic syntax with the
  8.     exception  of the  X command,  wich has  no meaning  in a compiled
  9.     language.
  10.  
  11.     Released in Denmark on June 3rd, 1990 as part of PingAnsi 1.30.
  12.  
  13.     By  using this  material You  assume FULL  responsibility for  ANY
  14.     consequences - direct or indirect - thereof. Any dispute regarding
  15.     this  material shall  be setteled  by Danish  law and  in a Danish
  16.     Court.
  17.  
  18.      (Sigh!)
  19.  
  20.     This source  may NOT be  used by Lawyers,  Politicians or, persons
  21.     engaged  in any  other form  of terrorism.  Otherwise the usage is
  22.     free.
  23.  
  24.     This  source  may  be  freely  distributed  as  long  as no fee is
  25.     charged.
  26.  
  27.     Please direct any comments, corrections, modifications via netmail
  28.     to:
  29.  
  30.                       Ping Hansen - Fido Net 2:231/62.58
  31.  
  32. *)
  33. Unit Musica;
  34.  
  35. Interface
  36.  
  37. Uses Dos, TpCrt;{CRT will do as well}
  38.  
  39. Const
  40.   MaxPlayBuffer       = 64;
  41.   { set this to true to disable background processing of sound }
  42.   NoBackground        : Boolean = False;
  43.   { If this is set stuff will WAIT for room in play buffer before returning }
  44.   WaitForSpace        : Boolean = True;
  45.  
  46. Var
  47.   BackGroundPlayHook  : Procedure(Tone, Duration : Word);
  48.   PlayBuffer          : Array[0..MaxPlayBuffer] Of
  49.     Record
  50.       Tone,
  51.       Duration            : Word;
  52.     End;
  53.  
  54. Procedure Play(St : String);
  55. Procedure PurgePlayBuffer;
  56. Function PlayBufferEmpty : Boolean;
  57. Function PlayBufferFull : Boolean;
  58.   {$F+}
  59. Procedure Stuff(Tone, Time : Word);
  60.   {$F-}
  61. Function GrabTimer  : Boolean;
  62.   {$F+}
  63. Procedure ReleaseTimer;
  64.   {$F-}
  65.  
  66.   {-----------------------------------------------------------------------}
  67.  
  68. Implementation
  69.  
  70. Const
  71.   Timer0              = 0;
  72.   FirstPlay           : Word = 0; { buffer Pointer }
  73.   LastPlay            : Word = 1; { buffer Pointer }
  74.   TimerMode           : Byte = 0; { saved mode for the timer }
  75.  
  76. Var
  77.   SaveExitProc        : Pointer;
  78.   SaveTimerInt        : Pointer;
  79.  
  80.   {-----------------------------------------------------------------------}
  81.  
  82.   Procedure Play(St : String);
  83.  
  84.   Const
  85.     Notes               : Array[1..84] Of Word =
  86.     { C    C#,D-  D    D#,E-  E     F    F#,G-  G    G#,A-  A    A#,B-  B  }
  87.     (0065, 0070, 0073, 0078, 0082, 0087, 0093, 0098, 0104, 0110, 0117, 0123,
  88.      0131, 0139, 0147, 0156, 0165, 0175, 0185, 0196, 0208, 0220, 0233, 0247,
  89.      0262, 0277, 0294, 0311, 0330, 0349, 0370, 0392, 0415, 0440, 0466, 0494,
  90.      0523, 0554, 0587, 0622, 0659, 0698, 0740, 0784, 0831, 0880, 0932, 0987,
  91.      1047, 1109, 1175, 1245, 1329, 1397, 1480, 1568, 1661, 1760, 1865, 1976,
  92.      2093, 2217, 2349, 2489, 2637, 2794, 2960, 3136, 3322, 3520, 3729, 3951,
  93.      4186, 4435, 4699, 4978, 5274, 5588, 5920, 6272, 6645, 7040, 7459, 7902);
  94.     MusicType           : Byte = 7; {Normal - note plays for 7/8 of time}
  95.     Tempo               : Word = 120; {120 beats per minute}
  96.     StdNoteLength       : Word = 4; {Quarter note}
  97.     Octave              : Word = 3; {Third octave}
  98.     BackGround          : Boolean = False; {Mn is default}
  99.  
  100.   Var
  101.     PlayTime, IdleTime,
  102.     DotTime, TempTime,
  103.     NoteLength, Note,
  104.     Index               : Word;
  105.     Ch                  : Char;
  106.  
  107.     {-------------}
  108.  
  109.     Function Numerical(Var Index : Word) : Word;
  110.  
  111.     Var
  112.       n                   : Word;
  113.     Begin
  114.       n := 0;
  115.       While (Index <= Length(St)) And (St[Index] In ['0'..'9']) Do
  116.       Begin
  117.         n := n * 10 + Ord(St[Index]) - Ord('0');
  118.         Inc(Index)
  119.       End;
  120.       Numerical := n;
  121.     End {Numerical} ;
  122.  
  123.     {-------------}
  124.  
  125.     Procedure CheckDots(Var Index : Word);
  126.  
  127.     Begin
  128.       While (Index <= Length(St)) And ((St[Index] = '.') Or (St[Index] = ',')) Do
  129.       Begin
  130.         DotTime := DotTime + DotTime Div 2;
  131.         Inc(Index)
  132.       End;
  133.     End {CheckDots} ;
  134.  
  135.     {-------------}
  136.  
  137.   Begin                           {Play subroutine}
  138.     Index := 1;
  139.     While Index < Length(St) Do
  140.     Begin
  141.       NoteLength := StdNoteLength;
  142.       DotTime := 1000;
  143.       Ch := Upcase(St[Index]);
  144.       Case Ch Of
  145.         'A'..'G' :
  146.           Begin                   {read note}
  147.             Note := Pos(Ch, 'CcDdEFfGgAaB');
  148.             Inc(Index);
  149.  
  150.             {Check for sharp or flat}
  151.             If Index <= Length(St) Then
  152.               Case St[Index] Of
  153.                 '#', '+' :
  154.                   Begin
  155.                     Inc(Note);
  156.                     Inc(Index);
  157.                   End;
  158.                 '-' :
  159.                   Begin
  160.                     Dec(Note);
  161.                     Inc(Index);
  162.                   End;
  163.               End;
  164.  
  165.             {Check for length suffix}
  166.             If (Index <= Length(St)) And
  167.             (St[Index] In ['0'..'9']) Then
  168.             Begin
  169.               NoteLength := Numerical(Index);
  170.             End;
  171.             CheckDots(Index);
  172.  
  173.             {calculate periods}
  174.             TempTime := Round(DotTime / Tempo / NoteLength * 240);
  175.             PlayTime := Round(TempTime * MusicType / 8);
  176.             IdleTime := TempTime - PlayTime;
  177.  
  178.             {Play the note}
  179.             If BackGround
  180.             Then
  181.             Begin
  182.               BackGroundPlayHook(Notes[Note + Octave * 12], PlayTime);
  183.               If IdleTime <> 0 Then BackGroundPlayHook(0, IdleTime);
  184.             End
  185.             Else
  186.             Begin
  187.               Sound(Notes[Note + Octave * 12]);
  188.               Delay(PlayTime);
  189.               If IdleTime <> 0 Then
  190.               Begin
  191.                 NoSound;
  192.                 Delay(IdleTime)
  193.               End;
  194.             End;
  195.             {}
  196.             {Check for ^C or Ctl-Break}
  197.             If keypressed And (ReadKey = ^C) Then
  198.             Begin
  199.               NoSound;
  200.               Exit;
  201.             End;
  202.             {}
  203.           End;
  204.         '<' :
  205.           Begin                   {step octave down}
  206.             If Octave > 0 Then Dec(Octave);
  207.             Inc(Index);
  208.           End;
  209.         '>' :
  210.           Begin                   {step octave up}
  211.             If Octave < 6 Then Inc(Octave);
  212.             Inc(Index);
  213.           End;
  214.         'L' :
  215.           Begin                   {set notelength}
  216.             Inc(Index);
  217.             StdNoteLength := Numerical(Index);
  218.             If (StdNoteLength < 1) Or (StdNoteLength > 64) Then
  219.               StdNoteLength := 4;
  220.           End;
  221.         'M' :
  222.           Begin                   {determine music type}
  223.             Inc(Index);
  224.             If (Index <= Length(St)) Then
  225.             Begin
  226.               Case Upcase(St[Index]) Of
  227.                 'S' : MusicType := 6; {music staccato}
  228.                 'N' : MusicType := 7; {music normal}
  229.                 'L' : MusicType := 8; {music legato}
  230.                 'B' : BackGround := True; {enable background buffering}
  231.                 'F' : BackGround := False; {disable do.}
  232.               End;
  233.               Inc(Index);
  234.             End;
  235.           End;
  236.         'O' :
  237.           Begin                   {set octave}
  238.             Inc(Index);
  239.             Octave := Numerical(Index);
  240.             If Octave > 6 Then Octave := 6;
  241.           End;
  242.         'P' :
  243.           Begin                   {pause}
  244.             NoSound;
  245.             Inc(Index);
  246.             NoteLength := Numerical(Index);
  247.             If (NoteLength < 1) Or (NoteLength > 64) Then
  248.               NoteLength := StdNoteLength;
  249.             CheckDots(Index);
  250.  
  251.             {calculate pause}
  252.             IdleTime := DotTime Div Tempo * (240 Div NoteLength);
  253.  
  254.             {execute pause}
  255.             If BackGround
  256.             Then BackGroundPlayHook(0, IdleTime)
  257.             Else Delay(IdleTime);
  258.           End;
  259.         'T' :
  260.           Begin                   {set tempo}
  261.             Inc(Index);
  262.             Tempo := Numerical(Index);
  263.             If (Tempo < 32) Or (Tempo > 255) Then
  264.               Tempo := 120;
  265.           End;
  266.         'N' :
  267.           Begin                   {play note #nn}
  268.             Inc(Index);
  269.             Note := Numerical(Index);
  270.             If (Note < 1) Then Note := 1;
  271.             If (Note > 84) Then Note := 84;
  272.             CheckDots(Index);
  273.  
  274.             {calculate periods}
  275.             TempTime := Round(DotTime / Tempo / NoteLength * 240);
  276.             PlayTime := Round(TempTime * MusicType / 8);
  277.             IdleTime := TempTime - PlayTime;
  278.  
  279.             {Play the note}
  280.             If BackGround
  281.             Then
  282.             Begin
  283.               BackGroundPlayHook(Notes[Note + Octave * 12], PlayTime);
  284.               If IdleTime <> 0 Then BackGroundPlayHook(0, IdleTime);
  285.             End
  286.             Else
  287.             Begin
  288.               Sound(Notes[Note + Octave * 12]);
  289.               Delay(PlayTime);
  290.               If IdleTime <> 0 Then
  291.               Begin
  292.                 NoSound;
  293.                 Delay(IdleTime)
  294.               End;
  295.             End;
  296.           End;
  297.         Else                      {garbage collector}
  298.           Inc(Index);             {pollution, Just dump it}
  299.       End;
  300.     End {While} ;
  301.     NoSound;                      {we are finished}
  302.   End {Play} ;
  303.  
  304.   {-----------------------------------------------------------------------}
  305.  
  306.   {$F+}
  307.   Procedure DummyStuff(Tone, Duration : Word);
  308.     {$F-}
  309.     {dummy background}
  310.   Begin
  311.     If Tone <> 0
  312.     Then Sound(Tone)
  313.     Else NoSound;
  314.     Delay(Duration);
  315.   End {DummyStuff} ;
  316.  
  317.   {-------------------------------------------------------------------------}
  318.  
  319.   Procedure PurgePlayBuffer;
  320.  
  321.   Begin
  322.     Inline($FA); {CLI}
  323.     FillChar(PlayBuffer, SizeOf(PlayBuffer), 0);
  324.     FirstPlay := 0;
  325.     LastPlay := 1;
  326.     Inline($FB); {STI}
  327.   end {PurgePlayBuffer} ;
  328.  
  329.   {-------------------------------------------------------------------------}
  330.  
  331.   Function PlayBufferEmpty : Boolean;
  332.  
  333.   Begin
  334.     PlayBufferEmpty := (FirstPlay = LastPlay);
  335.   End {PlayBufferEmpty} ;
  336.  
  337.   {-------------------------------------------------------------------------}
  338.  
  339.   Function PlayBufferFull : Boolean;
  340.  
  341.   Begin
  342.     PlayBufferFull := (LastPlay = FirstPlay - 1) Or
  343.     ((LastPlay = MaxPlayBuffer) And (FirstPlay = 1));
  344.   End {PlayBufferFull} ;
  345.  
  346.   {-------------------------------------------------------------------------}
  347.  
  348.   {$F+}
  349.   Procedure Stuff(Tone, Time : Word);
  350.     {$F-}
  351.  
  352.     { Place a note in background buffer. }
  353.  
  354.   Begin
  355.     If NoBackground Then
  356.     Begin
  357.       If Tone <> 0 Then Sound(Tone);
  358.       Delay(Time);
  359.       Exit;
  360.     End;
  361.     While WaitForSpace And PlayBufferFull Do {} ;
  362.     If                            {(LastPlay <> FirstPlay - 1) And
  363.     ((LastPlay <> MaxPlayBuffer) Or (FirstPlay <> 1))} Not PlayBufferFull Then
  364.     Begin
  365.       PlayBuffer[LastPlay].Tone := Tone;
  366.       PlayBuffer[LastPlay].Duration := Time;
  367.       Inc(LastPlay);
  368.       If LastPlay > MaxPlayBuffer Then LastPlay := 1;
  369.     End;
  370.   End {Stuff} ;
  371.  
  372.   {-------------------------------------------------------------------------}
  373.  
  374.   Procedure InitTimer(Timer, Mode : Byte; Count : Word);
  375.  
  376.   Var
  377.     Tics                : LongInt Absolute $40 : $6C;
  378.     t                   : LongInt;
  379.  
  380.   Begin
  381.     t := Tics;
  382.     While t = Tics Do {} ;        { wait for clock tick }
  383.     Inline($FA);                  {CLI}
  384.     Port[$43] := Mode;
  385.     Port[$40 + Timer] := Lo(Count);
  386.     Port[$40 + Timer] := Hi(Count);
  387.     Inline($FB);                  {STI}
  388.   End;
  389.  
  390.   {-------------------------------------------------------------------------}
  391.  
  392.   Procedure NewTimer(BP : Word); Interrupt;
  393.  
  394.   Const
  395.     InTune              : Boolean = True;
  396.     TimerVar            : Word = 54; { no delay first time }
  397.     Count               : Word = 05;
  398.   Begin
  399.     Inc(TimerVar);
  400.     If TimerVar >= 55 Then
  401.     Begin
  402.       TimerVar := 0;
  403.       Inline($9C / $FF / $1E / SaveTimerInt); { Pushf/Call Far SaveTimer }
  404.     End
  405.     Else
  406.     Begin
  407.       Port[$20] := $20;           { Non speciffic EOI }
  408.     End;
  409.     Inline($FB);                  {STI}
  410.     If Count > 0 Then Dec(Count);
  411.     If Count = 0 Then
  412.     Begin
  413.       If InTune Then
  414.       Begin
  415.         InTune := False;
  416.         NoSound;
  417.       End;
  418.       If (LastPlay <> FirstPlay) Then
  419.       Begin
  420.         If (PlayBuffer[FirstPlay].Tone <> 0) Then
  421.         Begin
  422.           Sound(PlayBuffer[FirstPlay].Tone);
  423.           InTune := True;
  424.         End;
  425.         If (PlayBuffer[FirstPlay].Duration <> 0)
  426.         Then Count := PlayBuffer[FirstPlay].Duration;
  427.         Inc(FirstPlay);
  428.         If FirstPlay > MaxPlayBuffer Then FirstPlay := 1;
  429.       End;
  430.     End;
  431.   End {NewTimer} ;
  432.  
  433.   {-------------------------------------------------------------------------}
  434.  
  435.   {$F+}
  436.   Procedure ReleaseTimer;
  437.     {$F-}
  438.  
  439.     { unload the interrupt handler }
  440.  
  441.   Begin
  442.     { Reprogram the 8253 to a 55 ms period }
  443.     InitTimer(Timer0, $36, 0);
  444.     SetIntVec($8, SaveTimerInt);
  445.     ExitProc := SaveExitProc;
  446.     NoSound;
  447.     BackgroundPlayHook := DummyStuff;
  448.   End {ReleaseTimer} ;
  449.  
  450.   {-------------------------------------------------------------------------}
  451.  
  452.   Function GrabTimer  : Boolean;
  453.  
  454.   Begin
  455.     GrabTimer := True;
  456.     FillChar(PlayBuffer, SizeOf(PlayBuffer), 0);
  457.     GetIntVec($8, SaveTimerInt);
  458. (*
  459.   Port[$43] := $E2;        { readback command. Timer 0, status. }
  460.   TimerMode := Port[$40] And $0F + $30;
  461.   if (TimerMode <> $36)
  462.   then GrabTimer := False
  463.   else
  464. *)
  465.     Begin
  466.       SaveExitProc := ExitProc;
  467.       InitTimer(Timer0, $36, $04A8);
  468.       SetIntVec($8, @NewTimer);
  469.       SaveExitProc := ExitProc;
  470.       ExitProc := @ReleaseTimer;
  471.       BackgroundPlayHook := Stuff;
  472.     (*
  473.     Stuff(10, 100); {void attempt to fix problem with first note}
  474.     *)
  475.     End;
  476.   End {GrabTimer} ;
  477.  
  478.   {-----------------------------------------------------------------------}
  479.  
  480. Begin
  481.   BackGroundPlayHook := DummyStuff;
  482. End.
  483.